#include "cbb_mem.h"
#include <stdint.h>
#include <string.h>

/* ---------------------------------- 静态内存分配 ---------------------------------- */

#if STATIC_MEM_SIZE > 0

// 静态内存池
static uint8_t u8SMem[S_MEM_SIZE];  

// 已使用的内存大小
uint32_t u32SMemUsed;               

void* cbb_s_malloc(uint32_t size){
    
    // 输入参数检查
    if(0 == size) return NULL;
    
    // 内存分配失败
    if(u32SMemUsed + size > S_MEM_SIZE) return NULL;
    
    // 内存分配成功
    uint8_t* pu8Ret = u8SMem + u32SMemUsed;
    u32SMemUsed += size;

    // 返回分配的内存指针
    return pu8Ret;
}

uint32_t cbb_get_s_mem_left(void){
    // 返回剩余内存大小
    return S_MEM_SIZE - u32SMemUsed;
}

uint32_t cbb_get_s_mem_used(void){
    // 返回已使用的内存大小
    return u32SMemUsed;
}

void cbb_clear_s_mem(void){
    // 内存清零
    memset(u8SMem,0,S_MEM_SIZE);
    u32SMemUsed = 0;
}

#endif

/* ---------------------------------- 动态内存分配 ---------------------------------- */

#if DYNAMIC_MEM_SIZE > 0

#define UNLOCKED  0                     // 未上锁
#define LOCKED    1                     // 已上锁
#define UNUSED  0                       // 未使用
#define USED    1                       // 已使用
#define USED_BIT_MASK  (1U << 31)       // 用于标记是否使用的位掩码（最高位）
#define BLOCK_SIZE_MASK (0x7FFFFFFFU)   // 用于标记块大小的位掩码（低31位）

// 动态内存块结构体
typedef struct A_Memory_Link {
    struct A_Memory_Link* pvNext; // 指向下一个内存块
    uint32_t u32BlockSize;   // 空闲块大小
}A_Memory_Link_t;

// 动态内存池
uint8_t u8AMem[A_MEM_SIZE];

// 动态内存块头指针
A_Memory_Link_t* pstAHead = NULL;

// 锁定状态
uint8_t u8Locked = UNLOCKED;

// 内存块结构体大小
#define LINK_SIZE sizeof(A_Memory_Link_t)

/**
 * @brief 获取块大小
 */
static uint32_t get_block_size(uint32_t u32Value){
    // 获取块大小
    return u32Value & BLOCK_SIZE_MASK;
}

/**
 * @brief 获取是否被使用
 */
static uint8_t get_used_status(uint32_t u32Value){
    // 获取是否被使用
    return (u32Value & USED_BIT_MASK) ? USED : UNUSED;
}

/**
 * @brief 组合块大小和使用状态
 * @param u32BlockSize 块大小
 * @param u8Used 使用状态
 */
static uint32_t combine_size_used(uint32_t u32BlockSize, uint8_t u8Used){

    // 合并块大小和使用状态
    uint32_t u32Value = u32BlockSize;

    // 标识是否被使用
    if(USED == u8Used) u32Value |= USED_BIT_MASK;

    // 返回数值
    return u32Value;
}

/**
 * @brief 合并空闲内存块
 */
static void
combine_mem(void){

    // 遍历内存块 -- 组合内存碎片
    A_Memory_Link_t* pstCurLink = pstAHead;
    while(NULL != pstCurLink){

        // 获取内存块使用状态
        uint8_t u8Used = get_used_status(pstCurLink->u32BlockSize);

        // 获取内存块大小
        uint32_t u32Size = get_block_size(pstCurLink->u32BlockSize);

        // 内存块被使用，跳过
        if(USED == u8Used) goto next;

        // 是否是最后一个节点
        if(pstCurLink->pvNext == NULL) break;

        // 内存块未使用，获取下一个节点
        A_Memory_Link_t* pstNextLink = pstCurLink->pvNext;

        // 获取内存块使用状态
        uint8_t u8NextUsed = get_used_status(pstNextLink->u32BlockSize);

        // 获取内存块大小
        uint32_t u32NextSize = get_block_size(pstNextLink->u32BlockSize);

        // 相邻块被使用，跳过
        if(USED == u8NextUsed) goto next;

        // 合并内存块
        uint32_t u32NewSize = u32Size + u32NextSize + LINK_SIZE;

        // 更新节点大小
        pstCurLink->u32BlockSize = combine_size_used(u32NewSize, UNUSED);

        // 更新链表
        pstCurLink->pvNext = pstNextLink->pvNext;

        // 下一个节点
        next:
            pstCurLink = pstCurLink->pvNext;

    }
}

void* cbb_a_malloc(uint32_t size){

    // 初始化内存块
    if(NULL == pstAHead) {
        // 清除内存
        cbb_clear_a_mem();

        // 申请头节点
        A_Memory_Link_t* pstLink = (A_Memory_Link_t*)u8AMem;
        pstAHead = pstLink;

        // 初始化头节点
        pstAHead->pvNext        = NULL;
        pstAHead->u32BlockSize  = combine_size_used(A_MEM_SIZE - LINK_SIZE,
                                                    UNUSED);
    }

    // 输入参数检查
    if(0 == size || LOCKED == u8Locked) return NULL;

    // 锁定内存
    u8Locked = LOCKED;

    // 申请到的地址
    uint8_t* u8Addr = NULL;

    // 遍历内存块
    A_Memory_Link_t* pstLink = pstAHead;
    while(NULL != pstLink){

        // 获取内存块使用状态
        uint8_t u8Used = get_used_status(pstLink->u32BlockSize);

        // 获取内存块大小
        uint32_t u32BlockSize = get_block_size(pstLink->u32BlockSize);

        // 找到一个空闲块且空闲块大小足够申请
        if(UNUSED != u8Used || u32BlockSize < size) goto next;

        // 存在内存碎片，且碎片支持再次分割
        if(size != u32BlockSize && u32BlockSize - size > LINK_SIZE) {

            // 申请节点
            A_Memory_Link_t* pstNewLink = (A_Memory_Link_t*)((uint8_t*)pstLink + size);

            // 初始化新节点
            pstNewLink->u32BlockSize = combine_size_used(u32BlockSize - size - LINK_SIZE,
                                                            UNUSED);   
            
            // 更新链表
            pstNewLink->pvNext = pstLink->pvNext;
            pstLink->pvNext = pstNewLink;
        }

        // 更新块大小
        pstLink->u32BlockSize = combine_size_used(size, USED);

        // 指向申请到的内存块的起始地址
        u8Addr = (uint8_t*)pstLink + LINK_SIZE;

        // 退出循环
        break;

        // 下一个节点
        next:
            pstLink = pstLink->pvNext;

    }

    // 解锁内存
    u8Locked = UNLOCKED;

    // 返回申请到的地址
    return (void*)u8Addr;
}

uint8_t cbb_a_free(void* pvAddr){

    // 输入参数检查
    if(NULL == pvAddr || NULL == pstAHead || LOCKED == u8Locked) return 0;

    // 判断内存是否在区域内
    if((uint8_t*)pvAddr < u8AMem + LINK_SIZE || (uint8_t*)pvAddr >= (u8AMem + A_MEM_SIZE - LINK_SIZE)) return 0;

    // 锁定内存
    u8Locked = LOCKED;

    // 指向申请到的内存块的起始地址
    A_Memory_Link_t* pstLink = (A_Memory_Link_t*)((uint8_t*)pvAddr - LINK_SIZE);

    // 获取内存块大小
    uint32_t u32BlockSize = get_block_size(pstLink->u32BlockSize);

    // 更新内存块状态未使用
    pstLink->u32BlockSize = combine_size_used(u32BlockSize, UNUSED);

    // 遍历内存块，组合内存碎片
    combine_mem();

    // 解锁内存
    u8Locked = UNLOCKED;

    // 返回成功
    return 1;
}

 uint32_t cbb_get_a_mem_left(void){

    // 记录剩余的内存块大小
    uint32_t u32Left = 0;

    // 遍历内存块
    A_Memory_Link_t* pstLink = pstAHead;
    while(NULL != pstLink){

        // 获取内存块使用状态
        uint8_t u8Used = get_used_status(pstLink->u32BlockSize);

        // 获取内存块大小
        uint32_t u32BlockSize = get_block_size(pstLink->u32BlockSize);

        // 找到一个空闲块       
        if(UNUSED == u8Used) u32Left += u32BlockSize;

        // 下一个节点
        pstLink = pstLink->pvNext;
    }

    // 返回剩余内存大小
    return u32Left;
 }

 uint32_t cbb_get_a_mem_used(void){
    // 返回已使用的内存大小
    return A_MEM_SIZE- cbb_get_a_mem_left();
 }
 
 void cbb_clear_a_mem(void){

    // 清空内存
    memset(u8AMem,0,A_MEM_SIZE);

    // 初始化头节点
    pstAHead = NULL;
}

#endif
